#pragma once
#include "requestor.hpp"

namespace BRpc
{
    namespace Client
    {
        class Register
        {
        public:
            using ptr = std::shared_ptr<Register>;

        public:
            Register(Requestor::ptr requestor) : _requestor(requestor) {}
            bool registerMethod(const BaseConnection::ptr &conn, const std::string &method, const Address &host)
            {
                auto req_msg = MessageFactory::create<ServiceRequest>();
                req_msg->setId(UUID::uuid());
                req_msg->setHost(host);
                req_msg->setMethod(method);
                req_msg->setMType(MType::REQ_SERVICE);
                req_msg->setOptype(ServiceOptype::SERVICE_REGISTRY);
                BaseMessage::ptr rsp_msg;
                bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), rsp_msg);
                if (ret == false)
                {
                    ELOG("%s 服务注册失败！", method.c_str());
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(rsp_msg);
                if (service_rsp.get() == nullptr)
                {
                    ELOG("响应类型向下转换失败！");
                    return false;
                }
                if (service_rsp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务注册失败，原因：%s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                return true;
            }
            
        private:
            Requestor::ptr _requestor;
        };
        class MethodHosts
        {
        public:
            using ptr = std::shared_ptr<MethodHosts>;

        public:
            MethodHosts() : _idx(0) {}
            MethodHosts(const std::vector<Address> &host) : _hosts(host), _idx(0) {}
            void appendHost(const Address &host)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _hosts.emplace_back(host);
                }
            }
            void removeHost(const Address &host)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    for (auto it = _hosts.begin(); it != _hosts.end(); it++)
                    {
                        if (*it == host)
                        {
                            _hosts.erase(it);
                            return;
                        }
                    }
                }
            }
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _idx++ % _hosts.size();
                _idx %= _hosts.size(); // 防止使用太多次超过了极限值了
                return _hosts[it];
            }
            bool empty()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            std::vector<Address> _hosts; // 存储着所有提供了这个方法的服务器的地址
            size_t _idx;                 // 用于轮询访问服务器
        };
        class Discoverer
        {
        public:
            using ptr = std::shared_ptr<Discoverer>;
            using offlineCb=std::function<void(const Address&)>;
        public:
            Discoverer(const Requestor::ptr &requestor, const offlineCb &cb) : 
                    _requestor(requestor), _off_cb(cb){}
            bool serviceDiscover(const BaseConnection::ptr &conn, const std::string &method, Address &host)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _method_hosts.find(method);
                    if (it != _method_hosts.end())
                    {
                        if (it->second->empty() == false) // 有提供者解决方法 直接返回地址
                        {
                            host = it->second->chooseHost(); // host是一个输出形参数
                            return true;
                        }
                    }
                }
                // 当前服务的提供者为空 发送一个发现服务请求
                auto msg_req = MessageFactory::create<ServiceRequest>();
                msg_req->setId(UUID::uuid());
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setMethod(method);
                msg_req->setOptype(ServiceOptype::SERVICE_DISCOVERY);
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->send(conn, msg_req, msg_rsp);
                if (ret == false)
                {
                    ELOG("服务发现失败！");
                    return false;
                }
                
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (!service_rsp)
                {
                    ELOG("服务发现失败！响应类型转换失败！");
                    return false;
                }
                if (service_rsp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务发现失败！%s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                // 能走到这里，代表当前是没有对应的服务提供主机的
                std::unique_lock<std::mutex> lock(_mutex);
                //将回应信息的host字段提取出来
                auto method_host = std::make_shared<MethodHosts>(service_rsp->hosts()); //没太看明白获得了这个host到底有什么用
                if (method_host->empty())//host字段为空 说明没有提供者可以提供这个方法的服务
                {
                    ELOG("%s 服务发现失败！没有能够提供服务的主机！", method.c_str());
                    return false;
                }
                //将host字段设置进方法提供者管理中
                _method_hosts[method] = method_host;
                host = method_host->chooseHost();//选择一个host
                return true;
            }
            //服务上线 下线时会发送通知 根据通知来更改自己的方法管理器;
            void onServiceRequest(const BaseConnection::ptr&conn,const ServiceRequest::ptr&msg)
            {
                //判断是上线还是下线 都不是就不处理
                auto optype=msg->optype();
                std::string method=msg->method();
                std::unique_lock<std::mutex> lock(_mutex);
                //上线
                if(optype==ServiceOptype::SERVICE_ONLINE)
                {
                    auto it=_method_hosts.find(method);
                    if(it==_method_hosts.end())//没有找到就创建
                    {
                        auto method_host=std::make_shared<MethodHosts>();
                        method_host->appendHost(msg->host());
                        _method_hosts[method]=method_host;
                    }
                    else  it->second->appendHost(msg->host());//找到了就添加
                }
                //下线
                else if(optype==ServiceOptype::SERVICE_OFFLINE)
                {
                    auto it=_method_hosts.find(method);
                    if(it==_method_hosts.end())//没找到就不管
                    {
                        return;
                    }
                    it->second->removeHost(msg->host());
                    _off_cb(msg->host());//调用下线回调函数 删除客户端中的链接
                }
            }
        private:
            std::unordered_map<std::string, MethodHosts::ptr> _method_hosts;
            offlineCb _off_cb;
            std::mutex _mutex;
            Requestor::ptr _requestor;
        };
    }
}